package xframe.util;

import java.io.BufferedWriter;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;

public class WhoIS {
    public final   int PORT =  80;
    public   boolean TRACE = false;
    public   final java.text.SimpleDateFormat yeardf = new java.text.SimpleDateFormat("yyyy");
    public final   int                        WHOIS_PORT = 43;    
    
    
 public   String whois2(String ip) throws Exception
    {
        if ( ip == null )
        {
            throw new NullPointerException( "ip address should not be null." );
        }
        
        InetAddress inetAddr = InetAddress.getByName(ip);
        ip = inetAddr.getHostAddress();
        
        if ( ip.startsWith( "10." ) || ip.startsWith( "172.16." ) || ip.startsWith( "192.168." ) )
        {
            return "Warning : Private network IP address.";
        }
        else if ( ip.startsWith( "127." ) )
        {
            return "Warning : Loopback Address.";
        }
        else if ( ip.startsWith( "224." ) || ip.startsWith( "239." ) )
        {
            return "Warning : Multicast Address.";
        }
        else if ( ip.startsWith( "240." ) || ip.startsWith( "255." ) )
        {
            return "Warning : Experimental Address.";
        }
            

        try
        {
             return whoisSend2( "whois.nic.or.kr", ip, "기관" );
        }
        catch ( Exception e )
        {
            try
            {
                String msg = whoisSend2( "ws.arin.net", ip, "OrgName" );

                if ( msg == null )
                    return "";
                else if ( msg.indexOf( "reserved for special purposes" ) != -1 )
                {
                    return "reserved for special purposes";
                }
                else if ( msg.indexOf( "APNIC" ) != -1 )
                {
                    msg = whoisSend2( "whois.apnic.net", ip, "netname:" );
                }

                return msg;
            }
            catch ( Exception e2 )
            {
                throw e2;
            }
        }
    }

    private   String whoisSend2(String Server, String ipAddr, String return_ok_str) throws Exception
    {
        Socket s = new Socket( Server, WHOIS_PORT );
        StringBuffer buf = new StringBuffer();
        String msg = null;

        InputStream in = null;
        OutputStream out = null;

        try
        {
            in = s.getInputStream();
            out = s.getOutputStream();

            PrintWriter pw = new PrintWriter( new BufferedWriter( new OutputStreamWriter( out ) ) );

            pw.println( ipAddr );
            pw.flush();

            String line = null;
            while ( (line = read_line2( in )) != null )
            {
                if ( TRACE )
                    System.err.println( line );
                
                buf.append(line);
                buf.append("\n");
            }
            
            msg = buf.toString().trim(); 
            
            if ( msg.length() == 0 )
            {
                throw new Exception( "unexcepted header data format" );
            }

        }
        finally
        {
            if ( in != null )
                try
                {
                    in.close();
                }
                catch ( Exception e )
                {
                }
            if ( out != null )
                try
                {
                    out.close();
                }
                catch ( Exception e )
                {
                }
            if ( s != null )
                try
                {
                    s.close();
                }
                catch ( Exception e )
                {
                }
        }

        int x = msg.indexOf( return_ok_str );

        if ( x == -1 )
            throw new Exception( msg );

        return getline2( msg.substring(x) );

    }

    public   String getline2(String s)
    {
        if ( s == null )
            return null;
        
        StringBuffer buf = new StringBuffer();
        for ( int i = 0; i < s.length(); i++ )
        {
            int x = s.indexOf( '\n' );
            if ( x == -1 )
            {
                buf.append( s );
                break;
            }
            else if ( x == s.length() - 1 )
            {
                buf.append( s );
                break;
            }
            else
            {
                String tmpLine = s.substring( 0, x + 1 );
                if ( tmpLine.trim().length() == 0 )
                {
                    break;
                }
                
                buf.append( tmpLine );
                s = s.substring( x + 1 );
            }
        }
        
        return buf.toString();
    }

    /**
     * Read a line of text. A line is considered to be terminated by a line feed
     * ('\n') or a carriage return followed immediately by a linefeed.
     * 
     * @return A String containing the contents of the line, not including any
     *         line-termination characters, or null if the end of the stream has
     *         been reached
     * 
     * @exception IOException If an I/O error occurs
     */
    private   String read_line2(InputStream in) throws Exception
    {
        java.io.ByteArrayOutputStream bout = new java.io.ByteArrayOutputStream();
        boolean eof = false;
        while ( true )
        {
            int b = in.read();
            if ( b == -1 )
            {
                eof = true;
                break;
            }
            if ( b != '\r' && b != '\n' )
                bout.write( (byte) b );
            if ( b == '\n' )
                break;
        }
        bout.flush();
        if ( eof && bout.size() == 0 )
            return null;
        // Or return ""; ? what's fit for you?
        return bout.toString();
    }
    
    
        
  public   String whois(String ip)  throws Exception{
        if(ip == null) throw new NullPointerException("ip address should not be null.");
        
        if(ip.equals("127.0.0.1") ) return "Warning: Local loopback IP.";
        else if(ip.startsWith("10.") || ip.startsWith("192.") || ip.startsWith("127.") )  return "Private network IP address.";

        try{
            return extract_after_colon(whois_kr(ip),5);
        }
        catch(Exception e){
            try{
                String msg =  whois_arin(ip);
                if (msg == null) return "";
                else if( msg.indexOf("reserved for special purposes") != -1 ) {
                    return "reserved for special purposes";
                }
                else if( msg.indexOf("APNIC") != -1){
                    msg = whois_apnic(ip);
                    msg = extract_after_colon(msg, 3);
                }
                else {
                    msg = extract_after_colon(msg, 7);
                }

                return msg;
            }
            catch(Exception e2){
                throw e2;
            }
        }
    }

    public   String whois_kr(String ip) throws Exception
    {
        String SERVER =  "whois.nic.or.kr";
        if(TRACE) System.err.print("> login to http://" + SERVER + "/result.php");
        Socket socket = new Socket(SERVER,PORT);
        if(TRACE) System.err.println("connected");
        InputStream in = null;
        OutputStream out = null;
        String msg = null;
        try{
            in = socket.getInputStream();
            out = socket.getOutputStream();

            try{
                java.util.Hashtable data = new java.util.Hashtable();
                data.put("domain_name", ip);  
                msg = http_post_send(SERVER, in,out,"/result.php",url_encoding(data),"기관", 5);
                
            }
            catch(Exception e){
                throw e;
            }
        }
        finally {
            if ( in != null ) try{in.close();}catch(Exception e){}
            if ( out != null ) try{out.close();}catch(Exception e){}
            if ( socket != null ) try{socket.close();}catch(Exception e){}
        }
        return msg;
    }

    public   String whois_arin(String ip) throws Exception
    {
        String SERVER =  "ws.arin.net";
        if(TRACE) System.err.print("> login to http://" + SERVER + "/cgi-bin/whois.pl");
        Socket socket = new Socket(SERVER,PORT);
        if(TRACE) System.err.println("connected");
        InputStream in = null;
        OutputStream out = null;
        String msg = null;
        try{
            in = socket.getInputStream();
            out = socket.getOutputStream();

            try{
                java.util.Hashtable data = new java.util.Hashtable();
                data.put("queryinput", ip);  
                msg = http_post_send(SERVER, in,out,"/cgi-bin/whois.pl",url_encoding(data),"OrgName", 21);
                msg = ignore_html(msg);
                //msg = extract_after_colon(msg);
                
            }
            catch(Exception e){
                throw new Exception(ignore_html(e.getMessage()));
            }
        }
        finally {
            if ( in != null ) try{in.close();}catch(Exception e){}
            if ( out != null ) try{out.close();}catch(Exception e){}
            if ( socket != null ) try{socket.close();}catch(Exception e){}
        }
        return msg;
    }

    public   String whois_apnic(String ip) throws Exception
    {
        String SERVER =  "www.apnic.net";
        if(TRACE) System.err.print("> login to http://" + SERVER + "/apnic-bin/whois.pl");
        Socket socket = new Socket(SERVER,PORT);
        if(TRACE) System.err.println("connected");
        InputStream in = null;
        OutputStream out = null;
        String msg = null;
        try{
            in = socket.getInputStream();
            out = socket.getOutputStream();

            try{
                java.util.Hashtable data = new java.util.Hashtable();
                data.put("searchtext", ip);
                //data.put("form_type", "advanced");
                //data.put("full_query_string","");
                //data.put("inverse_attributes", "None");
                //data.put("object_type", "All");
                //data.put(".cgifields","object_type");
                //data.put("reverse_delegation_domains", "ON");
                //data.put("ip_search_lvl", "-M");
                msg = http_post_send(SERVER, in,out,"/apnic-bin/whois.pl",url_encoding(data),"netname:", 3);
                msg = ignore_html(msg);
                //msg = extract_after_colon(msg);
                
            }
            catch(Exception e){
                throw new Exception(ignore_html(e.getMessage()));
            }
        }
        finally {
            if ( in != null ) try{in.close();}catch(Exception e){}
            if ( out != null ) try{out.close();}catch(Exception e){}
            if ( socket != null ) try{socket.close();}catch(Exception e){}
        }
        return msg;
    }

    private   String url_encoding(java.util.Hashtable hash){
        if ( hash == null ) throw new IllegalArgumentException("argument is null");
        java.util.Enumeration enum_e = hash.keys();
        StringBuffer buf = new StringBuffer();
        boolean isFirst = true;
        while(enum_e.hasMoreElements()){
          if (isFirst) isFirst = false;
          else buf.append('&');
          String key = (String)enum_e.nextElement();
          String value = (String)hash.get(key);
          buf.append(java.net.URLEncoder.encode(key));
          buf.append('=');
          buf.append(java.net.URLEncoder.encode(value));
        }
        return buf.toString();
    }
    
    private   String http_post_send(String SERVER, InputStream in, OutputStream out, String url, String body, String return_ok_str, int returnline ) throws Exception
    {
        PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(out)));
        String header = 
            "POST " + url + " HTTP/1.0\n" +
            "Accept-Language: ko\n" +
            "Content-Type: application/x-www-form-urlencoded\n" +
            "Accept-Encoding: gzip, deflate\n" +
            "User-Agent: WHOIS_API(http://www.javaservice.net)\n" +
            "Host: " + SERVER + "\n" +
            "Content-Length: " + body.getBytes().length + "\n" +
            //"Cookie: " + sessionid + "\n" +
            "Connection: Keep-Alive\n" +
            "Cache-Control: no-cache\n";
        if(TRACE){
          System.err.println("> -----------------------------------------------------------------------------");
          System.err.print(header);
          System.err.println(); // dummy line
        }
        pw.print(header);
        pw.println(); // dummy line
        pw.flush();

        if(TRACE) System.err.println(body);
        pw.print(body);
        pw.flush();
        if(TRACE){
          System.err.println("> -----------------------------------------------------------------------------");
          System.err.println("> header & data flushed");
          System.err.println("> now, wait response .....");
          System.err.println("> -----------------------------------------------------------------------------");
        }

        StringBuffer buf = new StringBuffer();
        int length = -1;
        String line = null;
        while( ( line = read_line(in)) != null ) {
            if(TRACE) System.err.println(line);
            if ( line.length() == 0 ) break;
            int x = line.indexOf("Content-Length:");
            if ( x > -1 ) {
                length = Integer.valueOf(line.substring(x+16)).intValue();
                if(TRACE) System.err.println("LENGTH:" + length);
            }
        }
        if ( line == null ) throw new Exception("unexcepted header data format");

        if ( length != -1 ) {
            // (mayby) HTTP 1.1 "Content-Length" field exist in the header.
            byte[] d = read_data(in,length);
            buf.append(new String(d));
            if(TRACE) System.err.println(new String(d));
        }
        else {
            // NO "Content-Length" field exist in the header
            String length_hex = read_line(in);
            if ( length_hex == null ) throw new Exception("there is no HTTP body data");
            try{
                length = Integer.valueOf(length_hex.trim(),16).intValue();
            }
            catch(Exception e){
                //This means, the following data is just normal BODY data.
                //And length is still -1.
            }
            if ( length != -1 ) {
                // Yap. WebSphere 3.0.x, 3.5.x
                read_line(in); // dummy line
                byte[] d = read_data(in,length);
                buf.append(new String(d));
                if(TRACE) System.err.println(new String(d));
            }
            else {
                byte[] d = read_data(in);
                buf.append(new String(d));
                if(TRACE) System.err.println(new String(d));
            }
        }
        String msg = buf.toString();
        int x = msg.indexOf(return_ok_str);

        if(x == -1) throw new Exception(msg);

        return getline(msg.substring(x), returnline);
    }

    public   String ignore_html(String s){
        if( s == null ) return null;
        StringBuffer buf = new StringBuffer();
        while(true){
            int x = s.indexOf('<');
            if( x == -1) {
                buf.append(s);
                break;
            }
            else {
                buf.append(s.substring(0, x));
                s = s.substring(x+1);
                int y = s.indexOf('>');
                if(y != -1) s = s.substring(y+1);
                else break;
            }
        }
        return buf.toString();
    }

    public   String ignore_line(String s){
        if( s == null ) return null;
        StringBuffer buf = new StringBuffer();
        for(int i=0;i<s.length();i++){
            char c = s.charAt(i);
            switch(c){
                case '\r':
                case '\n': break;
                case '\t': buf.append(' '); break;
                default: buf.append(c); break;
            }
        }
        return buf.toString();
    }
        
    public   String extract_after_colon(String s, int line){
        if( s == null ) return null;
        StringBuffer buf = new StringBuffer();
        for(int i=0;i<line;i++){
            int x = s.indexOf(':');
            if( x == -1 ) {
                buf.append(s.trim());
                break;
            }
            else if( x == s.length()-1 ) break;
            else {
                s = s.substring(x+1);
                int y = s.indexOf('\n');
                if( y == -1 || y == s.length()-1 ) {
                    buf.append(s.trim() + ' ');
                    break;
                }
                else {
                    buf.append(s.substring(0,y).trim() + ' ');
                    s = s.substring(y+1);
                }
            }
        }
        return buf.toString();
    }

    public   String getline(String s, int n){
        if( s == null ) return null;
        StringBuffer buf = new StringBuffer();
        for(int i=0;i<n;i++){
            int x = s.indexOf('\n');
            if(x == -1) {
                buf.append(s);
                break;
            }
            else if( x == s.length()-1) {
                buf.append(s);
                break;
            }
            else {
                buf.append(s.substring(0, x+1));
                s = s.substring(x+1);
            }
        }
        return buf.toString();
    }
    
    public   final int INTPUTSTREAM_READ_RETRY_COUNT = 10;

    /** 
     * The <code>read_data</code> method of <code>SocketUtil</code> reads the
     * specified length of bytes from the given input stream.
     *
     * @param      in   an inputstream
     * @param      len  the number of bytes read.
     * @return     The specified number of bytes read until the end of
     *             the stream is reached.
     * @exception  Exception  if an I/O error or unexpected EOF occurs
     */
    private   byte[] read_data(InputStream in, int len) throws Exception {
        java.io.ByteArrayOutputStream bout = new java.io.ByteArrayOutputStream();
        int bcount = 0;
        byte[] buf = new byte[len];
        int read_retry_count = 0;
        while( bcount < len ) {
            int n = in.read(buf,0, len-bcount < len ? len-bcount : len );
            // What would like to do if you've got an unexpected EOF before
            // reading all data ?
            //if (n == -1) break;
            if ( n == -1 ) throw 
                 new java.io.IOException("inputstream has returned an unexpected EOF");

            if ( n == 0 && (++read_retry_count == INTPUTSTREAM_READ_RETRY_COUNT) )
                throw new java.io.IOException("inputstream-read-retry-count( " +
                    INTPUTSTREAM_READ_RETRY_COUNT + ") exceed !");
            if ( n != 0 ) {
                bcount += n;
                bout.write(buf,0,n);
            }
        }
        bout.flush();
        return bout.toByteArray();
    }
    
    /** 
     * The <code>read_data</code> method of <code>SocketUtil</code> reads all
     * the bytes from the given inputstream until the given input stream 
     * has not returned an EOF(end-of-stream) indicator.
     *
     * @param      in   an inputstream
     * @return     all bytes read if the end of the stream is reached.
     * @exception  Exception  if an I/O error occurs
     */
    private   byte[] read_data(InputStream in) throws Exception {
        java.io.ByteArrayOutputStream bout = new java.io.ByteArrayOutputStream();
        int bcount = 0;
        byte[] buf = new byte[2048];
        int read_retry_count = 0;
        while( true ) {
            int n = in.read(buf);
            if (n == -1) break;
            if ( n == 0 && (++read_retry_count == INTPUTSTREAM_READ_RETRY_COUNT) )
                throw new java.io.IOException("inputstream-read-retry-count( " +
                    INTPUTSTREAM_READ_RETRY_COUNT + ") exceed !");
            if ( n != 0 ) {
                bcount += n;
                bout.write(buf,0,n);
            }
        }
        bout.flush();
        return bout.toByteArray();
    }
  
    /**
     * Read a line of text.  A line is considered to be terminated by a line
     * feed ('\n') or a carriage return followed immediately by a linefeed.
     *
     * @return     A String containing the contents of the line, not including
     *             any line-termination characters, or null if the end of the
     *             stream has been reached
     *
     * @exception  IOException  If an I/O error occurs
     */
    private String read_line(InputStream in) throws Exception {
        java.io.ByteArrayOutputStream bout = new java.io.ByteArrayOutputStream();
        boolean eof = false;
        while( true ) {
            int b = in.read();
            if (b == -1) { eof = true; break;}
            if ( b != '\r' && b != '\n' ) bout.write((byte)b);
            if (b == '\n') break;
        }
        bout.flush();
        if ( eof && bout.size() == 0 ) return null; 
        //Or return ""; ? what's fit for you?
        return bout.toString();
    }


}
